#ifndef __MODULE_HOLDER_H__
#define __MODULE_HOLDER_H__

#include <string>
#include <vector>

#include "dll_holder.h"
#include "shared_ptr.h"
#include "ifaces_core.h"
#include "ref_obj_qi_ptr.h"


namespace Boss
{

  BOSS_DECLARE_RUNTIME_EXCEPTION(ModuleHolder)

  class ModuleHolder
    : private NonCopyable
  {
  public:
    typedef SharedPtr<DllHolder> DllHolderPtr;
    typedef RefObjPtr<IBase> IBasePtr;
    typedef std::vector<std::string> GuidPool;

    ModuleHolder(DllHolderPtr dll)
      : Dll(dll)
    {
      if (!Dll.Get())
        throw ModuleHolderException("Dll is empty");
    }
    const std::string& GetModuleGuid() const
    {
      if (ModuleGuid.empty())
      {
        typedef TGuid (*PFNGetModuleGuid)();
        ModuleGuid = Dll->GetProc<PFNGetModuleGuid>("GetModuleGuid")();
      }
      return ModuleGuid;
    }
    const std::string& GetModuleName() const
    {
      if (ModuleName.empty())
      {
        typedef const char* (*PFNGetModuleName)();
        ModuleName = Dll->GetProc<PFNGetModuleName>("GetModuleName")();
      }
      return ModuleName;
    }
    const GuidPool& GetCoClassIds() const
    {
      if (CoClassIds.empty())
      {
        typedef unsigned (*PFNGetCoClassCount)();
        typedef TGuid (*PFNGetCoClassId)(unsigned);
        unsigned Count = Dll->GetProc<PFNGetCoClassCount>("GetCoClassCount")();
        GuidPool Ids;
        for (unsigned i = 0 ; i < Count ; ++i)
          Ids.push_back(Dll->GetProc<PFNGetCoClassId>("GetCoClassId")(i));
        CoClassIds = Ids;
      }
      return CoClassIds;
    }
    IBasePtr CreateObject(TGuid classId)
    {
      typedef ULong (*PFNCreateObject)(TGuid, TPtr *);
      IBasePtr Ret;
      if (Dll->GetProc<PFNCreateObject>("CreateObject")(classId, reinterpret_cast<TPtr *>(Ret.GetPPtr())) != rcOk)
        throw ModuleHolderException("Can't create object");
      return IBasePtr(Ret);
    }
    template <typename T>
    RefObjQIPtr<T> CreateObject(TGuid classId)
    {
      RefObjQIPtr<T> Ret(CreateObject(classId));
      if (!Ret.Get())
        throw ModuleHolderException("Interface not found");
      return Ret;
    }
    ULong GetModuleCounter() const
    {
      typedef ULong (*PFNGetModuleCounter)();
      return Dll->GetProc<PFNGetModuleCounter>("GetModuleCounter")();
    }
    void SetEnviroment(IBasePtr enviroment)
    {
      typedef void (*PFNSetEnviroment)(TPtr);
      return Dll->GetProc<PFNSetEnviroment>("SetEnviroment")(enviroment.Get());
    }

  private:
    mutable DllHolderPtr Dll;
    mutable std::string ModuleName;
    mutable std::string ModuleGuid;
    mutable GuidPool CoClassIds;
  };

}

#endif  // !__MODULE_HOLDER_H__
